package com.gqyin.demo.aspect;

import com.gqyin.demo.annotation.Loggable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.logging.LogLevel;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author guiqing.yin
 * 2024-04-09 14:58
 */
@Aspect
@Component
public class LogAspect {

    public static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

    @Pointcut("@annotation(com.gqyin.demo.annotation.Loggable)")
    public void logPointcut() {
    }

    @Around("logPointcut()")
    public Object logMethodCall(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = String.format("%s#%s", className, signature.getName());

        Loggable anno = null;
        for (Method method : signature.getDeclaringType().getDeclaredMethods()) {
            if (method.getName().equals(signature.getName())) {
                anno = method.getAnnotation(Loggable.class);
                break;
            }
        }
        if (anno == null) {
            return null;
        }
        LogLevel logLevel = anno.level();

        String callMessage = String.format("Method %s is called with arguments: %s", methodName, Arrays.toString(args));
        switch (logLevel) {
            case FATAL:
            case ERROR:
                LOGGER.error(callMessage);
                break;
            case WARN:
                LOGGER.warn(callMessage);
                break;
            case INFO:
                LOGGER.info(callMessage);
                break;
            case DEBUG:
                LOGGER.debug(callMessage);
                break;
            case TRACE:
                LOGGER.trace(callMessage);
                break;
            case OFF:
            default:
                break;
        }

        // 执行目标方法
        Object result;
        try {
            result = joinPoint.proceed(args);
        } catch (Exception e) {
            LOGGER.error("Method {} occurs exception: {}", methodName, e.getMessage());
            throw e;
        }

        String returnMessage = String.format("Method %s returned value: %s", methodName, result);
        switch (logLevel) {
            case FATAL:
            case ERROR:
                LOGGER.error(returnMessage);
                break;
            case WARN:
                LOGGER.warn(returnMessage);
                break;
            case INFO:
                LOGGER.info(returnMessage);
                break;
            case DEBUG:
                LOGGER.debug(returnMessage);
                break;
            case TRACE:
                LOGGER.trace(returnMessage);
                break;
            case OFF:
            default:
                break;
        }
        return result;
    }
}
